package docker

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"code.gitea.io/gitea/modules/log"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/client"
	"github.com/docker/docker/pkg/stdcopy"
	"github.com/docker/go-connections/nat"
)

// CreateDockerClient 创建Docker客户端
func CreateDockerClient(ctx context.Context) (*client.Client, error) {
	log.Info("检查 Docker 环境")
	// 1. 检查 Docker 环境
	dockerSocketPath, err := GetDockerSocketPath()
	if err != nil {
		return nil, err
	}
	log.Info("dockerSocketPath: %s", dockerSocketPath)
	// 2. 创建docker client 并且检查是否运行

	opts := []client.Opt{
		client.FromEnv,
	}
	if dockerSocketPath != "" {
		opts = append(opts, client.WithHost(dockerSocketPath))
	}
	cli, err := client.NewClientWithOpts(opts...)

	if err != nil {
		return nil, err
	}
	_, err = cli.Ping(ctx)
	if err != nil {
		return nil, fmt.Errorf("docker未运行, %w", err)
	}
	return cli, nil
}

// GetDockerSocketPath 获取Docker Socket路径
func GetDockerSocketPath() (string, error) {
	// 检查环境变量
	socket, found := os.LookupEnv("DOCKER_HOST")
	if found {
		return socket, nil
	}
	// 检查常见的Docker socket路径
	socketPaths := []string{
		"/var/run/docker.sock",
		"/run/podman/podman.sock",
		"$HOME/.colima/docker.sock",
		"$XDG_RUNTIME_DIR/docker.sock",
		"$XDG_RUNTIME_DIR/podman/podman.sock",
		`\\.\pipe\docker_engine`,
		"$HOME/.docker/run/docker.sock",
	}
	// 测试Docker默认路径
	for _, p := range socketPaths {
		if _, err := os.Lstat(os.ExpandEnv(p)); err == nil {
			if strings.HasPrefix(p, `\\.\`) {
				return "npipe://" + filepath.ToSlash(os.ExpandEnv(p)), nil
			}
			return "unix://" + filepath.ToSlash(os.ExpandEnv(p)), nil
		}
	}
	// 找不到
	return "", fmt.Errorf("Docker未安装")
}
func GetContainerID(cli *client.Client, containerName string) (string, error) {
	containerJSON, err := cli.ContainerInspect(context.Background(), containerName)
	if err != nil {
		return "", err
	}
	return containerJSON.ID, nil
}

func GetContainerStatus(cli *client.Client, containerID string) (string, error) {
	containerInfo, err := cli.ContainerInspect(context.Background(), containerID)
	if err != nil {
		return "", err
	}
	state := containerInfo.State
	return state.Status, nil
}
func PushImage(dockerHost string, username string, password string, registryUrl string, imageRef string) error {
	script := "docker " + "-H " + dockerHost + " login -u " + username + " -p " + password + " " + registryUrl + " "
	cmd := exec.Command("sh", "-c", script)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("%s \n 镜像登录失败: %s", string(output), err.Error())
	}
	// 推送到仓库
	script = "docker " + "-H " + dockerHost + " push " + imageRef
	cmd = exec.Command("sh", "-c", script)
	output, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("%s \n 镜像推送失败: %s", string(output), err.Error())
	}
	return nil
}

// PullImage 拉取Docker镜像
func PullImage(ctx context.Context, cli *client.Client, dockerHost, imageName string) error {

	reader, err := cli.ImagePull(ctx, imageName, types.ImagePullOptions{})
	if err != nil {
		return err
	}
	defer reader.Close()

	// 读取并丢弃输出，确保拉取完成
	_, err = io.Copy(io.Discard, reader)
	return err
}

// CreateAndStartContainer 创建并启动容器
func CreateAndStartContainer(ctx context.Context, cli *client.Client, imageName string, cmd []string, env []string, binds []string, ports nat.PortSet, containerName string) error {

	// 配置容器
	config := &container.Config{
		Image: imageName,
		Env:   env,
	}
	if ports != nil {
		config.ExposedPorts = ports
	}
	if cmd != nil {
		config.Cmd = cmd
	}

	hostConfig := &container.HostConfig{
		Binds: binds,
		RestartPolicy: container.RestartPolicy{
			Name: "always", // 设置为 always
		},
		PublishAllPorts: true,
	}

	// 创建容器
	resp, err := cli.ContainerCreate(ctx, config, hostConfig, nil, nil, containerName)
	if err != nil {
		return err
	}

	// 启动容器
	return cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
}

// DeleteContainer 停止并删除指定名称的容器
func DeleteContainer(ctx context.Context, cli *client.Client, containerName string) error {

	// 首先尝试停止容器
	timeout := 10
	err := cli.ContainerStop(ctx, containerName, container.StopOptions{
		Timeout: &timeout,
	})
	if err != nil {
		// 如果容器已经停止或不存在，继续执行删除操作
		// 这里不返回错误，因为我们的目标是删除容器
	}

	// 删除容器
	err = cli.ContainerRemove(ctx, containerName, types.ContainerRemoveOptions{
		Force: true, // 强制删除，即使容器正在运行
	})
	if err != nil {
		return err
	}

	return nil
}
func IsContainerNotFound(err error) bool {
	if client.IsErrNotFound(err) {
		return true
	}
	return false
}

// 获取容器端口映射到了主机哪个端口，参数: DockerClient、containerName、容器端口号
func GetMappedPort(ctx context.Context, containerName string, port string) (uint16, error) {
	// 创建 Docker 客户端
	cli, err := CreateDockerClient(ctx)
	if err != nil {
		return 0, err
	}
	// 获取容器 ID
	containerID, err := GetContainerID(cli, containerName)
	if err != nil {
		return 0, err
	}
	// 获取容器详细信息
	containerJSON, err := cli.ContainerInspect(context.Background(), containerID)
	if err != nil {
		return 0, err
	}

	// 获取端口映射信息
	portBindings := containerJSON.NetworkSettings.Ports
	for containerPort, bindings := range portBindings {
		for _, binding := range bindings {
			log.Info("容器端口 %s 映射到主机 %s 端口 %s \n", containerPort, binding.HostIP, binding.HostPort)
			if containerPort.Port() == port {
				port_64, err := strconv.ParseUint(binding.HostPort, 10, 16)
				if err != nil {
					return 0, err
				}
				return uint16(port_64), nil
			}
		}
	}
	return 0, fmt.Errorf("容器未开放端口 %s", port)
}
func ExecCommandInContainer(ctx context.Context, cli *client.Client, containerName string, command string) (string, error) {
	containerID, err := GetContainerID(cli, containerName)
	if err != nil {
		log.Info("创建执行实例失败", err)
		return "", err
	}
	cmdList := []string{"sh", "-c", command}
	execConfig := types.ExecConfig{
		Cmd:          cmdList,
		AttachStdout: true,
		AttachStderr: true,
	}
	// 创建执行实例
	exec, err := cli.ContainerExecCreate(context.Background(), containerID, execConfig)
	if err != nil {
		log.Info("创建执行实例失败", err)
		return "", err
	}
	// 附加到执行实例
	resp, err := cli.ContainerExecAttach(context.Background(), exec.ID, types.ExecStartCheck{})
	if err != nil {
		log.Info("命令附加到执行实例失败", err)
		return "", err
	}
	defer resp.Close()
	// 启动执行实例
	err = cli.ContainerExecStart(context.Background(), exec.ID, types.ExecStartCheck{})
	if err != nil {
		log.Info("启动执行实例失败", err)
		return "", err
	}
	// 自定义缓冲区
	var outBuf, errBuf strings.Builder

	// 读取输出
	_, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader)
	if err != nil {
		log.Info("Error reading output for command %v: %v\n", command, err)
		return "", err
	}
	return outBuf.String() + errBuf.String(), nil
}
